﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace IOP.Extension
{
    /// <summary>
    /// 表达式树扩展
    /// </summary>
    public static class ExpressionTreeExtension
    {

        /// <summary>
        /// 获取属性值
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static object GetPropertyValue<TSource>(this TSource source, string propertyName)
        {
            var type = typeof(TSource);
            ParameterExpression paraExpression = Expression.Parameter(type, "x");
            MemberExpression memExpression = Expression.Property(paraExpression, propertyName);
            LambdaExpression lambdaExpression = Expression.Lambda(typeof(Func<,>).MakeGenericType(new Type[] { type, memExpression.Type }), memExpression, paraExpression);
            var func = lambdaExpression.Compile();
            return func.DynamicInvoke(source);
        }

        /// <summary>
        /// 设置属性值
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        public static void SetPropertyValue<TSource>(this TSource source,string propertyName, object value)
        {
            var type = typeof(TSource);
            ParameterExpression paraExpression = Expression.Parameter(type, "x");
            MemberExpression memExpression = Expression.Property(paraExpression, propertyName);
            ConstantExpression constant = Expression.Constant(value);
            UnaryExpression convert = Expression.Convert(constant, type.GetProperty(propertyName).PropertyType);
            Expression assign = Expression.Assign(memExpression, convert);
            LambdaExpression lambdaExpression = Expression.Lambda(typeof(Func<,>).MakeGenericType(new Type[] { type, memExpression.Type }), assign, paraExpression);
            var func = lambdaExpression.Compile();
            func.DynamicInvoke(source);
        }

        /// <summary>
        /// 判断两个相同对象的属性值是否一致
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static bool AreEqual<TSource>(this TSource source, TSource target, string propertyName)
        {
            var type = typeof(TSource);
            if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException(nameof(propertyName));
            ParameterExpression sourceExpression = Expression.Parameter(type, "x");
            ParameterExpression targetExpression = Expression.Parameter(type, "y");
            PropertyInfo property = type.GetProperty(propertyName);
            MemberExpression originProperty = Expression.Property(sourceExpression, property);
            MemberExpression targetProperty = Expression.Property(targetExpression, property);
            BinaryExpression areSame = Expression.Equal(originProperty, targetProperty);
            Expression<Func<TSource, TSource, bool>> lambda = Expression.Lambda<Func<TSource, TSource, bool>>(areSame, sourceExpression, targetExpression);
            var func = lambda.Compile();
            return func(source, target);
        }

        /// <summary>
        /// 从源对象复制数据至新对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="target"></param>
        public static void CopyDataFromSource<T>(this T source, T target)
        {
            var type = typeof(T);
            ParameterExpression sourceExpression = Expression.Parameter(type, "x");
            ParameterExpression targetExpression = Expression.Parameter(type, "y");
            List<Expression> expressions = new List<Expression>();
            foreach (var property in type.GetProperties())
            {
                if (!property.CanWrite) continue;
                MemberExpression originProperty = Expression.Property(sourceExpression, property);
                MemberExpression sourceProperty = Expression.Property(targetExpression, property);
                BinaryExpression assign = Expression.Assign(sourceProperty, originProperty);
                expressions.Add(assign);
            }
            BlockExpression block = Expression.Block(expressions);
            Expression<Action<T, T>> lambda = Expression.Lambda<Action<T, T>>(block, sourceExpression, targetExpression);
            var func = lambda.Compile();
            func(source, target);
        }
    }
}
